//  IDVI 1.0 source copyright 1996 Garth A. Dickie
//
//  This source is free for non-commercial use.  No warranty, etc.
//  Please acknowledge reuse by including the line:
//
//  "Based in part on IDVI 1.0 source copyright 1996 Garth A. Dickie"
//
//  in your documentation and source code.  For commercial use or
//  distribution, please contact the author.  Please also send
//  questions, comments, bug reports, or fixes.
//
//  Best Regards,
//  Garth A. Dickie
//  dickie@elastic.avid.com

package ibook.v10.idvi.dvi;

import java.io.*;
import java.util.*;
import java.net.*;

import ibook.v10.idvi.*;
import ibook.v10.idvi.display.BlockRoot;
import ibook.v10.idvi.font.*;
import ibook.v10.idvi.io.*;

public class DVIDocument {

    //  Global document handling.  A document cache is kept.
    //
    //  This is useful because entering a page a second time does not use
    //  the existing applet.  Instead, a new applet is started each time.
    //  So the applets should avoid loading multiple copies of the same
    //  dvi file.

    final private static int    kDocumentCacheInitialSize = 13;

    private static Hashtable    documentCache;
    private String              key;            // note these are not static
    private int                 referenceCount; //

    public static synchronized DVIDocument getDocument(
            URL documentURL, String documentName, int dpi, URL fontURL, MessageContext context ) {
        
        if( documentCache == null )
            documentCache = new Hashtable( kDocumentCacheInitialSize );
        
        String key = documentURL.toString( ) + "." + dpi;
        DVIDocument result = ( DVIDocument )documentCache.get( key );

        if( result == null ) {
            try {
                context.showMessage( "Loading dvi file \"" + documentName + "\"" );
                InputStream stream = documentURL.openStream( );

                result = new DVIStreamDocument( stream, dpi, fontURL, context );
                result.referenceCount = 1;
                result.key = key;

                documentCache.put( key, result );
            } catch( Exception e ) {
                context.showMessage( "Exception while loading \"" + documentURL + "\"" );
                System.err.println( e );
            }
        } else
            result.referenceCount ++;
        
        return result;
    }

    public static synchronized void putDocument( DVIDocument document ) {
        if( -- document.referenceCount == 0 )
            documentCache.remove( document.key );
    }




    public BlockRoot getPage( int index ) {
        return null;
    }

    public int getPageCount( ) {
        return 0;
    }

    public int clipPageNumber( int pageNumber ) {
        return 0;
    }




    //  Named anchor management for multiple-age dvi files.
    //
    //  This should really block, waiting for the name to show up
    //  later in the file, instead of punting (see getPage implementation
    //  in DVIStreamDocument, for example).

    final private static int kNameBlockInitialSize = 13;

    private Hashtable   nameBlock = new Hashtable( kNameBlockInitialSize );

    public void addNameBlock( String name, int pageNumber ) {
        nameBlock.put( name, new Integer( pageNumber ));
    }

    public int getNameBlockPageNumber( String name ) throws DVIFormatException {
        Integer pageInteger = ( Integer ) nameBlock.get( name );
        
        if( pageInteger == null )
            throw new DVIFormatException( "link to non-existant anchor \"" + name + "\"." );
        
        return pageInteger.intValue( );
    }




    //  The constructor of a subclass should set the dots-per-inch, as well
    //  as a URL for a directory holding pk files.  The page parser will ask
    //  us for the conversion factor dimconv.

    private int         dpi;
    private URL         fontBase;
    private int         magnification;
    private double      dimconv;

    double getConversion( ) {
        return dimconv;
    }

    int getDPI( ) {
        return dpi;
    }

    protected void setDPI( int dpi ) {
        this.dpi = dpi;
    }

    protected void setFontBase( URL fontBase ) {
        this.fontBase = fontBase;
    }



    //  font handling for the document

    final private static int kDocumentFontInitialSize = 31;

    private Hashtable documentFont = new Hashtable( kDocumentFontInitialSize );

    DVIDocumentFont getFont( int number ) {
        return ( DVIDocumentFont )( documentFont.get( new Integer( number )));
    }

    void defineFont(
            int number, int checksum, int scale, int design, String name, MessageContext context ) {

        double magfactor = .001 * magnification * scale / design;
        double conversion = dimconv * scale / 0x100000;

        DVIFont font = DVIFont.getFont( fontBase, name, dpi, magfactor, context );

        documentFont.put( new Integer( number ), new DVIDocumentFont( font, conversion ));
    }

    public Enumeration getFonts( ) {
        return documentFont.elements( );
    }




    //  parsing the header of the document

    protected void parseHeader( DVIInputStream source )
            throws IOException, DVIFormatException {
        
        if( source.unsigned1( ) != DVIFormat.kMagicDVI )
            throw new DVIFormatException( "% is not a DVI file." );

        if( source.unsigned1( ) != DVIFormat.kMagicVersion )
            throw new DVIFormatException( "DVI file % has wrong version number." );

        int numerator = source.unsigned4( );
        int denominator = source.unsigned4( );
        magnification = source.unsigned4( );
        source.skip( source.unsigned1( )); // skip over comment

        // from the numerator, denominator, magnification, and dpi, we compute
        // a scaling factor from dvi units to pixels on the screen.  For example, for
        // 300 dpi display and units of pt, we usually get 4.1511, or 300 / 72.27.

        dimconv = (( double ) numerator * ( double ) magnification * ( double ) dpi * 65536.0 )
                / (( double ) denominator * 254000000.0 );
    }
}
